#!/usr/bin/env python

""" Wepye: The Web Python Editor """

import qweb
import glob
import mimetypes
import optparse
import os
import sys
from wepye_python import *
import pty
import time
import threading
import fcntl
import select
import signal

def debug(s):
    print s;

class SynchronizedMethod:
    def __init__(self,lock,orig):
        self.lock=lock
        self.orig=orig
    def __call__(self,*l):
        self.lock.acquire()
        r=self.orig(*l)
        self.lock.release()
        return r


class Multiplex:
    def __init__(self):
        self.proc = {}
        self.end_of_process="#WEPYE_END_OF_CHILD_PROCESS"
        self.alive = 1
        self.lock=threading.RLock()
        self.thread=threading.Thread(target=self.loop)
        self.buf=''
        for name in ['create','fds','read','write','stop', 'kill','run', 'dumphtml']:
            orig=getattr(self,name)
            setattr(self,name,SynchronizedMethod(self.lock,orig))
        self.thread.start()
    def create(self,src):
        self.buf=''
        pid, fd=pty.fork()
        inter = python_interpreter()
        if pid:
            fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK)
            self.proc[fd]={'pid': pid, 'buf':'', 'time': time.time()}
            if not self.run():
                self.alive = 1
            return fd
        if pid == 0:  # child
            try:
                fdl=[int(i) for i in os.listdir('/proc/self/fd')]
            except OSError:
                fdl = range(256)
            for  i in [ i for i in fdl if i>2 ]:
                try:
                    os.close(i)
                except OSError:
                    pass
            ret = inter.run_script(src);
            print ret
            print self.end_of_process
    def run(self, fd=0):
        if not fd:
            return self.alive;
        return fd in self.fds()
    def stop(self):
        self.alive = 0
    def fds(self):
        return self.proc.keys()
    def kill(self, fd):
        debug("try to kill fd: "+str(fd))
        if fd in self.proc:
            self.proc[fd]['time']=0
        t=time.time()
        for i in self.proc.keys():
            t0=self.proc[i]['time']
            if (t-t0)>120:
                try:
                    os.close(i)
                    debug("Do kill now!!");
                    os.kill(self.proc[i]['pid'],signal.SIGKILL)
                    os.waitpid(self.proc[i]['pid'], 0)
                except (IOError,OSError):
                    pass
                del self.proc[i]
                self.buf = self.buf + self.end_of_process
    def read(self, fd, max=65536):
        try:
            self.proc[fd]['time']=time.time()
            ret = os.read(fd, max)
        except (IOError,OSError):
            self.kill(fd)
        return ret
    def write(self, fd, data):
        try:
            os.write(fd, data)
        except (IOError,OSError):
            self.kill(fd)
    def dumphtml(self, req):
        if not req:
            return
        if len(self.buf) == 0:
            response = '<?xml version="1.0"?><idem></idem>'
        else:
            response = '<?xml version="1.0"?><p>'+self.buf+'</p>'
        debug("dumphtml response='"+response+"'");
        req.response_headers['Content-Type']='text/xml'
        req.write(response)
        req.response_gzencode=1
        self.buf=''

    def loop(self):
        while self.run():
            fds = self.fds()
            i,o,e=select.select(fds, [], [], 1.0)
            for fd in i:
                txt = self.read(fd);
                print txt
                self.buf =self.buf + txt
                if txt.strip().find(self.end_of_process) >= 0:
                    self.kill(fd)
            if len(i):
                time.sleep(0.002)
        for i in self.proc.keys():
            try:
                self.kill(i)
            except (IOError,OSError):
                pass


from wepye_commands import *

class Wepye:
    def __init__(self,cmd=None,index_file='wepye.html'):
        self.files = {}
        for ext in ['css', 'html', 'js']:
            for f in glob.glob('*.%s'%ext):
                self.files[f] = file(f).read()
        self.files['index']=file(index_file).read()
        self.mime = mimetypes.types_map.copy()
        self.mime['.html']= 'text/html; charset=UTF-8'
        self.multiplex = None
        self.session = {}
        self.init_commands()
    def init_commands(self):
        self.commands = []
        for c in Command.__subclasses__():
            self.commands.append(c())

    # called when XMLHtmlRequest send
    def __call__(self, environ, start_response):
        req = qweb.QWebRequest(environ, start_response,session=None)
        flag = False
        debug("Got request!!")
        if not self.multiplex:
            self.multiplex  = Multiplex()
        elif not self.multiplex.run():
            del self.multiplex
            self.multiplex = Multiplex();
        # Put every functions here
        for c in self.commands:
            flag = c.check_and_run(self, req)
            if flag:
                break
        # If there are no such functions send index.html (wepye.html)
        if not flag:
            n=os.path.basename(req.PATH_INFO)
            if n in self.files:
                req.response_headers['Content-Type'] = self.mime.get(os.path.splitext(n)[1].lower(), 'application/octet-stream')
                req.write(self.files[n])
            else:
                req.response_headers['Content-Type'] = 'text/html; charset=UTF-8'
                req.write(self.files['index'])
        return req

def main():
    parser = optparse.OptionParser()
    (o, a) = parser.parse_args()


    wepye  = Wepye();
    try:
        qweb.QWebWSGIServer(wepye,ip='localhost',port=3000,threaded=0).serve_forever()
    except Exception, e:
        print "Error Stop server!!"
        raise

if __name__ == '__main__':
    main()
